﻿using System.Data;
using System.Data.Common;
using CoreEx.Common.Proxy;
using LinFu.IoC.Configuration;
using LinFu.IoC.Interfaces;
using LinFu.IoC;
using LinFu.Proxy.Interfaces;
using IProxyFactory=LinFu.Proxy.Interfaces.IProxyFactory;

namespace CoreEx.Database.Client.Implementation
{
    /// <summary>
    /// A factory class that creates <see cref="IDbCommand"/> instances.
    /// </summary>
    [Factory(typeof(IDbCommand))]
    public class CommandFactory : IFactory<IDbCommand>
    {
        /// <summary>
        /// Creates a <see cref="IDbCommand"/> proxy instance.
        /// </summary>
        /// <param name="request">The <see cref="IFactoryRequest"/> instance that describes the requested service.</param>
        /// <returns>
        /// <see cref="IDbCommand"/>
        /// </returns>
        public IDbCommand CreateInstance(IFactoryRequest request)
        {
            //Get the connection info that corresponds to the service name.
            IDbCommand dbCommand = CreateCommand(request);            
            return dbCommand;                        
        }

        private static IDbCommand CreateCommand(IFactoryRequest request)
        {
            var connectionInfo = request.Container.GetService<DbConnectionInfo>(request.ServiceName);

            //Create a provider factory
            var providerFactory = DbProviderFactories.GetFactory(connectionInfo.ProviderName);
            
            //Create the actual command
            IDbCommand dbCommand =  providerFactory.CreateCommand();
            
            var proxyDbCommand = CreateCommandProxy(request, dbCommand, connectionInfo.Name);
            proxyDbCommand.Connection = CreateConnection(request, connectionInfo.Name);
            return proxyDbCommand;
        }

        private static IDbConnection CreateConnection(IFactoryRequest request, string connectionName)
        {
            IDbConnection dbConnection = null;
            if (request.Arguments.Length > 0)
                dbConnection = request.Arguments[0] as IDbConnection;
            if (dbConnection == null)
            {
                dbConnection = request.Container.GetService<IDbConnection>(connectionName);
                ((INamedConnection) dbConnection).IsInternal = true;
            }
            return dbConnection;
        }



        private static IDbCommand CreateCommandProxy(IFactoryRequest request, IDbCommand command, string connectionName)
        {
            var proxyFactory = request.Container.GetService<IProxyFactory>();

            var invokeWrapper = request.Container.GetService<IInvokeWrapper<IDbCommand>>(null,new object[]{connectionName, command});
            var dbCommandProxy = proxyFactory.CreateProxy<IDbCommand>(
                invokeWrapper, typeof(IActualTarget));
                                     
            return dbCommandProxy;            
        }
    }
}
